﻿using System;
using System.Collections.Generic;
using UnityEngine;

public class AStar : MonoBehaviour
{
    [Header("地图")]
    public int mapW;
    public int mapH;
    public AStarNode[,] maps;
    [Header("开放和关闭列表")]
    public List<AStarNode> openList;
    public List<AStarNode> closeList;
    public bool isEnemy = false;

    //初始化
    public void Init(AStarNode[,] _maps,int _mapW,int _mapH)
    {
        maps=_maps;
        mapW=_mapW;
        mapH=_mapH;
    }
    //寻找路径
    public List<AStarNode> SeekPath(int startX,int startY,int endX,int endY)
    {
        isEnemy=false;
        //起点和终点不能重合
        if (startX==endX&&startY==endY)
        {
            Debug.Log("起点和终点重合");
            return null;
        }
        //越界判断
        if(startX<0||startX>=mapW||startY<0||startY>=mapH)
        {
            Debug.Log("起点【"+startX+","+startY+"】坐标越界");
            return null;
        }
        if (endX<0||endX>=mapW||endY<0||endY>=mapH)
        {
            Debug.Log("终点【"+endX+","+endY+"】坐标越界");
            return null;
        }
        //阻挡判断
        if (maps[startX, startY].type==NodeType.Stop)
        {
            Debug.Log("【"+startX+","+startY+"】坐标阻挡");
            return null;
        }
        if (maps[endX, endY].type==NodeType.Stop)
        {
            Debug.Log("【"+endX+","+endY+"】坐标阻挡");
            return null;
        }
        //清空列表
        openList.Clear();
        closeList.Clear();
        //放入第一个节点到开放列表
        AStarNode startNode = maps[startX, startY];
        startNode.parent=null;
        closeList.Add(startNode);
        //开始循环
        while(true)
        {
            //遍历周围点(上下左右)
            SetNode(startNode, startNode.pos.x, startNode.pos.y+1, 1, endX, endY);
            SetNode(startNode, startNode.pos.x, startNode.pos.y-1, 1, endX, endY);
            SetNode(startNode, startNode.pos.x+1, startNode.pos.y, 1, endX, endY);
            SetNode(startNode, startNode.pos.x-1, startNode.pos.y, 1, endX, endY);
            //如果开放列表为空了，死循环
            if(openList.Count<=0)
            {
                Debug.Log("死路");
                return null;
            }
            //找到开放列表里面最少消耗点，放到关闭列表
            openList.Sort(SortList);
            startNode=openList[0];
            openList.RemoveAt(0);
            closeList.Add(startNode);
            //查看是否是终点
            if(startNode.pos.x==endX&&startNode.pos.y==endY)
            {
                //回溯列表
                List<AStarNode> list = new List<AStarNode>();
                list.Add(startNode);
                //回溯关闭列表
                while(startNode.parent!=null)
                {
                    startNode = startNode.parent;
                    list.Add(startNode);
                }
                //反转列表，去掉第一个值，返回列表
                list.Reverse();
                list.RemoveAt(0);
                return list;
            }
        }
    }
    //升序排序
    public int SortList(AStarNode a, AStarNode b)
    {
        //a>b,返回1，b排在a前面
        //a==b,返回0，ab位置不变
        //a<b,返回-1，a排在b前面
        return a.f.CompareTo(b.f);
    }
    //单个格子设置(父物体，此点，离父物体距离，终点)
    public void SetNode(AStarNode parent,int x,int y,float offset,int endX,int endY)
    {
        //判断越界
        if (x<0||x>=mapW||y<0||y>=mapH)
        {
            Debug.Log("【"+x+","+y+"】坐标越界");
            return;
        }
        //阻挡判断
        if (maps[x,y].type==NodeType.Stop)
        {
            Debug.Log("【"+x+","+y+"】坐标阻挡");
            return;
        }
        //如果是敌人
        if (maps[x,y].type==NodeType.Enemy)
        {
            if (x==endX&&y==endY)
            {
                isEnemy=true;
            }
            //只有这个点是终点才加入
            if (x!=endX||y!=endY)
            {
                Debug.Log("【" + x + "," + y + "】此点是Enemy,且不为终点");
                return;
            }
        }
        //计算消耗
        maps[x, y].g=parent.g+offset;
        maps[x, y].h=Math.Abs(endY-y)+Math.Abs(endX-x);
        maps[x, y].f=maps[x, y].g+maps[x, y].h + maps[x,y].weight;
        //放进开放列表,只有列表没有此点才添加
        if (!openList.Contains(maps[x, y])&&!closeList.Contains(maps[x,y]))
        {
            //设置父物体
            maps[x, y].parent = parent;
            openList.Add(maps[x, y]);
        }
    }
}